package terminal;

import java.awt.Font;
import console.*;
import console.window.*;
import java.io.*;
import java.util.*;
import gnu.io.*; /* for rxtxSerial library */

class PortCloser implements Runnable {
    SerialPort serialPort;
    PortCloser(SerialPort port) {
        serialPort = port;
    }
    public void run() {
        serialPort.close();
    } 
}

public class Terminal implements SerialPortEventListener {
    public native long readTimeStampCounter();
    public Kermit kermit;
    String logFileName = "";
    java.io.FileWriter logFile = null;
    volatile int gotAck=0;
    long mutexTime;
    de.mud.terminal.vt320 emulation;
    SwingMessageArea console;
    final de.mud.terminal.SwingTerminal terminal;

    CommPortIdentifier portId;
    Enumeration portList;

    public OutputStream outputStream;
    boolean outputBufferEmptyFlag = false;

    public InputStream inputStream;
    public SerialPort serialPort;
    Thread readThread;
    public int[] image;
    public byte[] binaryBuffer;
    int binaryBufferCount;
    public Object bufferLock = new Object();
    int binaryBufferSize;
    
    int escape_buffer[];
    volatile int escape_buffer_in;
    volatile int escape_buffer_out;
    boolean escaped;
    boolean gotEscapeCount;
    int escapeCount;

    boolean use_compression = false;
    int locate = 0;

    boolean kermitEnabled;

    public long rxCount = 0;
    public Object rxCountLock = new Object();

    static {
        File f;
        String s;
        if(System.getProperty("path.separator").equals(":")) {
            f=new File(System.getProperty("user.dir")+"/scripting-console/libtsc.so");
        } else {
            f=new File(System.getProperty("user.dir")+"/scripting-console/tsc.dll");
        }
        try {
            s=f.getCanonicalPath();
        } catch (IOException e) {s="";}
        if(f.exists()) {
            System.load(s);
        }
    }
    public void start(String defaultPort,int baudRate) {
        boolean portFound = false;
        int port_count;
        int i;
    
        portList = CommPortIdentifier.getPortIdentifiers();
        port_count = 0;
        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement();
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                port_count++;
                if (portId.getName().equals(defaultPort)) {
                    portFound = true;
                    break;
                }
            }
        }
        if (!portFound) {
            if(port_count==0) {
                javax.swing.JOptionPane.showMessageDialog(
                        null,
                        "No Serial Ports Found",
                        "Error",
                        javax.swing.JOptionPane.ERROR_MESSAGE);
                System.exit(-1);
            } else if(port_count>0) {
                Object[] possibilities;
                possibilities = new Object[port_count];
                portList = CommPortIdentifier.getPortIdentifiers();
                i=0;
                while (portList.hasMoreElements()) {
                    portId = (CommPortIdentifier) portList.nextElement();
                    if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                        defaultPort=portId.getName();
                        possibilities[i]= portId.getName();
                        i++;
                    }
                }
                if(port_count>1) {
                    defaultPort = (String)javax.swing.JOptionPane.showInputDialog(
                        null,
                        "ports found:",
                        "Select Communication Port",
                        javax.swing.JOptionPane.PLAIN_MESSAGE,
                        null,
                        possibilities,
                        possibilities[0]);
                }
                if ((defaultPort == null) || (defaultPort.length() == 0)) {
                    System.out.println("communication port not selected");
                    System.exit(-1);
                } else {
                    portList = CommPortIdentifier.getPortIdentifiers();
                    while (portList.hasMoreElements()) {
                        portId = (CommPortIdentifier) portList.nextElement();
                        if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                            if (portId.getName().equals(defaultPort)) {
                                portFound = true;
                                break;
                            }
                        }
                    }
                }
            }
        }

        try {
            try {
                serialPort = (SerialPort) portId.open("Terminal", 2000);
            } catch (PortInUseException e) {
                        System.out.println("couldn't open " + defaultPort);
                        javax.swing.JOptionPane.showMessageDialog(
                            null,
                            "couldn't open " + defaultPort + ", port in use",
                            "Error",
                            javax.swing.JOptionPane.ERROR_MESSAGE);
                    System.exit(-1);
            }
        } catch (java.lang.RuntimeException e) {
                System.out.println("couldn't open " + defaultPort);
                javax.swing.JOptionPane.showMessageDialog(
                    null,
                    "couldn't open " + defaultPort + ", runtime error",
                    "Error",
                    javax.swing.JOptionPane.ERROR_MESSAGE);
                System.exit(-1);
        }
    
    
        if(
            (serialPort.getBaudRate()!=baudRate)||
            (serialPort.getDataBits()!=SerialPort.DATABITS_8)||
            (serialPort.getStopBits()!=SerialPort.STOPBITS_1)||
            (serialPort.getParity()!=SerialPort.PARITY_NONE)) {
            try {
                serialPort.setSerialPortParams(
                            baudRate,
                            8, 
                            1, 
                            0);
            } catch (UnsupportedCommOperationException e)
                {System.out.println("setSerial didn't work");}
        }
    
        try {
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();
        } catch (IOException e) {}
    
        try {
            serialPort.addEventListener(this);
        } catch (TooManyListenersException e) {System.out.println("too many serial port listeners");}
        kermit = new Kermit(outputStream);

        escape_buffer = new int[0x100];
        escape_buffer_in=0;
        escape_buffer_out=0;
        escaped=false;
        gotEscapeCount=false;
        escapeCount=0;
    
        serialPort.notifyOnDataAvailable(true);
        try {
            serialPort.notifyOnOutputEmpty(true);
        } catch (Exception e) {
            System.out.println("Error setting event notification");
            System.out.println(e.toString());
            System.exit(-1);
        }
    
        Runtime.getRuntime().addShutdownHook(new Thread(new PortCloser(serialPort)));
    

    }
    public Terminal(SwingMessageArea console) {
        this.console = console;
        console.setFont(new Font("Courier",Font.PLAIN,console.getFont().getSize()));
        console.addFocusListener(new ConsoleFocusListener());
        console.setBackground(console.getBackground().brighter());
    

        emulation = new de.mud.terminal.vt320() {
                        public void keyTyped(int keyCode, char keyChar, int modifiers) {
                            boolean control = (modifiers & de.mud.terminal.VDUInput.KEY_CONTROL) != 0;
                            boolean shift = (modifiers & de.mud.terminal.VDUInput.KEY_SHIFT) != 0;
                            boolean alt = (modifiers & de.mud.terminal.VDUInput.KEY_ALT) != 0;
                            if((keyChar==java.awt.event.KeyEvent.VK_TAB)&&!shift&&!alt&&!control) {
                            } else {
                                super.keyTyped(keyCode,keyChar,modifiers);
                            }
                        }
                        public void keyPressed(int keyCode, char keyChar, int modifiers) {
                            boolean control = (modifiers & de.mud.terminal.VDUInput.KEY_CONTROL) != 0;
                            boolean shift = (modifiers & de.mud.terminal.VDUInput.KEY_SHIFT) != 0;
                            boolean alt = (modifiers & de.mud.terminal.VDUInput.KEY_ALT) != 0;
                            if((keyCode==java.awt.event.KeyEvent.VK_TAB)&&!shift&&!alt&&!control) {
                                terminal.transferFocus();
                            } else {
                                super.keyPressed(keyCode,keyChar,modifiers);
                            }
                        }
                        public void write(byte[] b) {
                            try {
                                outputStream.write(b);
                            } catch (IOException e) {
                                System.err.println("jta: error sending data: " + e);
                            }
                        }
                    };
    //  emulation.showCursor(false);
        emulation.setBufferSize(600);
        emulation.setCursorPosition(0,0);
        terminal = new de.mud.terminal.SwingTerminal(emulation);

        
 //       readThread = new Thread(this);
    
 //       readThread.start();
        kermitEnabled=true;

        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                if(logFile!=null) {
                    try {
                    logFile.flush();
                    logFile.close();
                    } catch (java.io.IOException e) { }
                    logFile=null;
                }
            }
          });
    }

    public SwingMessageArea getConsole() {
        return(console);
    }

    public de.mud.terminal.SwingTerminal getTerminal() {
        return(terminal);
    }

//    public void run() {
//    } 

    String hexIfNonPrintable(byte[] buf,int start,int count) {
        int i;
        int value;
        String cooked = "";
        for(i=0;i<count;i++) {  
            value=((int)buf[start+i])&0xff;
            /* printable characters, carriage return, linefeed, tab */
            if(((value>=0x20)&&(value<0x7f))/*||(value==0x0d)||(value==0x0a)*/||(value==0x09)) {
                cooked=cooked+((char)value);
            } else {
                cooked=cooked+"["+(((Integer.toHexString(value).length())==1)?"0":"")+Integer.toHexString(value)+"]\r\n";
            }
        }
        return(cooked);
    }

    protected void sendStringToTerminal(String s) {
        try {
            if(emulation!=null) {
                try {
                    emulation.windowBase = emulation.screenBase;
                    emulation.putString(s);
                } catch (java.lang.ArrayIndexOutOfBoundsException e) {
                }
            }
            if(logFile!=null) {
                s = s.replaceAll("\r","\n");
                s = s.replaceAll("\n\n","\n");
                s = s.replaceAll("\n","\r\n");
                logFile.write(s);
            }
        } catch (IOException e) {}
    }

    public void serialEvent(SerialPortEvent event) {
    switch (event.getEventType()) {

    case SerialPortEvent.BI:

    case SerialPortEvent.OE:

    case SerialPortEvent.FE:

    case SerialPortEvent.PE:

    case SerialPortEvent.CD:

    case SerialPortEvent.CTS:

    case SerialPortEvent.DSR:

    case SerialPortEvent.RI:

    case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
        break;

    case SerialPortEvent.DATA_AVAILABLE:
        byte[] readBuffer = new byte[1024*8];
        try {
        while (inputStream.available() > 0) {
            int numBytes = inputStream.read(readBuffer);
            int i=0;
            int kermitSkipCount=0;

            rxCount+=numBytes;
            if(numBytes!=0) {
                synchronized(rxCountLock) {
                    rxCountLock.notifyAll();
                }
            }
//            System.out.println();
//            System.out.print("\t\tbuffer: ");
//            for(i=0;i<numBytes;i++) {
//                if(readBuffer[i]==1) {
//                    System.out.print("<SOH>");
//                } else {
//                    if((readBuffer[i]>=32)&&(readBuffer[i]<127)) {
//                        System.out.print((char)readBuffer[i]);
//                    } else {
//                        System.out.print(".");
//                    }
//                }
//            }
//            i=0;
            if(kermitEnabled) {
                while(i<numBytes) {
//                    System.out.println();
//                    System.out.print("\t\t\tkermit: i:"+i+" numBytes:"+numBytes);
                    if(readBuffer[i]==Kermit.KERMIT_START_OF_HEADER || kermit.escaped) {
                        if(!kermit.escaped) {
//                            System.out.print(" SOH ");
                            kermit.initializePacket();
                            kermitSkipCount=kermit.handleIncomming(readBuffer,i+1,numBytes)+1;
                        } else {
//                            System.out.print(" ESC ");
                            kermitSkipCount=kermit.handleIncomming(readBuffer,i,numBytes);
                        }
//                        System.out.print("skip:"+kermitSkipCount);
                        for(int j=0;j<(numBytes-i-kermitSkipCount);j++) {
                            readBuffer[i+j]=readBuffer[i+j+kermitSkipCount];
                        }
                        numBytes-=kermitSkipCount;
                    }
                    i++;
                }
                i=0;
            } else {
                while(i<numBytes && escaped) {
                    if(binaryBufferCount<binaryBufferSize) {
                        if(readBuffer[i]==Kermit.KERMIT_START_OF_HEADER) {
                            System.out.println("got SOH in escape buffer");
                        }
                        binaryBuffer[binaryBufferCount]=readBuffer[i++];
                        if((binaryBufferCount+1)==binaryBufferSize) {
                            escaped=false;
                        }
                        binaryBufferCount++;
                        synchronized(bufferLock) {
                            bufferLock.notifyAll();
                        }
/*
*/
                    } else {
                        escaped=false;
                    }
                }
            }

//            System.out.println();
//            System.out.print("\t\tafter: ");
//            for(i=0;i<numBytes;i++) {
//                if(readBuffer[i]==1) {
//                    System.out.print("<SOH>");
//                } else {
//                    if((readBuffer[i]>=32)&&(readBuffer[i]<127)) {
//                        System.out.print((char)readBuffer[i]);
//                    } else {
//                        System.out.print(".");
//                    }
//                }
//            }
//            System.out.println();
//            System.out.flush();
//            i=0;

            if(!escaped && i<numBytes) {
                int index_current,index_start=i;
                for(index_current=i;index_current<(numBytes-i);index_current++) {
                    if(readBuffer[index_current]==0x1a) {
                        if(index_current>index_start) {
                            sendStringToTerminal(new String(readBuffer,index_start,index_current));
                        }
                        index_current++; /* skip over 0x1a */
                        index_start=index_current;
                        /* clear screen */
                        ((de.mud.terminal.vt320)terminal.getVDUBuffer()).putString("\f");
                        continue;
                    }
                    if(readBuffer[index_current]==0x1e) {
                        if(index_current>index_start) {
                            sendStringToTerminal(new String(readBuffer,index_start,index_current));
                        }
                        index_current++; /* skip over 0x1e */
                        index_start=index_current;
                        ((de.mud.terminal.vt320)terminal.getVDUBuffer()).putString("\u001b[1;1H");
                        /* goto 0,0 */
                        continue;
                    }
                }
                if(index_current>index_start) {
                    int jjj;
                    for(jjj=index_start;jjj<index_current;jjj++) {
                        if(readBuffer[jjj]==Kermit.KERMIT_START_OF_HEADER) {
                            System.out.println("sent SOH to terminal");
                        }
                    }
                    sendStringToTerminal(new String(readBuffer,index_start,index_current));
                }
            }


        } 
        } catch (IOException e) { System.out.println("%"); }
        break;
    }
    } 

    void putBuffer(int b) {
        escape_buffer[escape_buffer_in]=b;
        escape_buffer_in++;
        if(escape_buffer_in==escape_buffer.length)
            escape_buffer_in=0;
        if(escape_buffer_in==escape_buffer_out) {
            escape_buffer_out++;
            if(escape_buffer_out==escape_buffer.length)
                escape_buffer_out=0;
        }
    }
    int getBuffer() {
        int b;
        while(escape_buffer_empty());
        b=escape_buffer[escape_buffer_out];
        escape_buffer_out++;
        if(escape_buffer_out==escape_buffer.length)
            escape_buffer_out=0;
        return(b);
    }
    boolean escape_buffer_empty() {
        return(escape_buffer_in==escape_buffer_out);
    }

    public void clearResultBuffer() {
        escape_buffer_in=0;
        escape_buffer_out=0;
        escaped=false;
        gotEscapeCount=false;
        escapeCount=0;
    }
    public boolean resultAvailable() {
        return(escape_buffer_in!=escape_buffer_out);
    }

    public void sendCommand(int command) throws java.io.IOException {
        outputStream.write((byte)command);
    }

    public void sendCommand(int command,boolean waitForSilence) throws java.io.IOException {
        sendCommand(command);
        if(waitForSilence) {
            long savedRxCount;
            while(true) {
                savedRxCount=rxCount;/* wait for it to be set when a data is received */
                try {
                    synchronized(rxCountLock) {
                        rxCountLock.wait(500); /* wait at most 0.5 seconds */
                    }
                    if(rxCount==savedRxCount) {
                        break; /* detected silence for 0.5 seconds */
                    }
                } catch (InterruptedException e) { }
            }
        }
    }

    public void closeDown() {
        serialPort.close();
    }
    public void finalize() {
        serialPort.close();
    }
    int jiffyCount=0;
    public void sendBreak(int time) {
        serialPort.sendBreak(time);
    }
    public void setBaudRate(int baud) {
    try {
        serialPort.setSerialPortParams(
                        baud,
                        8, 
                        1, 
                        0);
    } catch (UnsupportedCommOperationException e)
        {System.out.println("setBaudRate didn't work");}
    }
    public long requestMutex(long mutexTime) {
        if(this.mutexTime==0) {
            this.mutexTime=mutexTime;
            terminal.setEnabled(false);
        }
        return(this.mutexTime);
    }
    public long releaseMutex(long mutexTime) {
        if(this.mutexTime==mutexTime) {
            this.mutexTime=0;
            terminal.setEnabled(true);
        }
        return(this.mutexTime);
    }
    public void setKermitEnabled(boolean b) {
        kermitEnabled=b;
    }
    public boolean getKermitEnabled() {
        return(kermitEnabled);
    }
    public void receiveFromDUT(int size) {
//        long tsc = readTimeStampCounter();
//        if(requestMutex(tsc)==tsc) {
          escaped=false;
          binaryBufferSize=0;
          binaryBufferCount = 0;
          binaryBuffer = new byte[size];
          binaryBufferSize = size;
          if(size>0) {
              escaped=true;
          } else {
              escaped=false;
          }
//        } else {
//          throw new java.lang.Exception();
//        }
    }
    public int getDUTBufferCount() {
        return(binaryBufferCount);
    }
    public int getDUTBufferSize() {
        return(binaryBufferSize);
    }
    public byte[] getDUTBuffer() {
        return(binaryBuffer);
    }
    public void flushSerial() throws java.io.IOException {
        outputStream.flush();
    }
    public String getLogFile() {
        return(logFileName);
    }
    public void setLogFile(String fileName,boolean appendFlag) throws java.io.IOException {
            if(fileName.length() == 0) {
                if(logFile!=null) {
                    logFile.flush();
                    logFile.close();
                    logFile=null;
                }
                logFileName="";
            } else {
                java.io.File newLogFile = new File(fileName);
                java.io.FileWriter fileWriter = new java.io.FileWriter(newLogFile,appendFlag);
                if(logFile!=null) {
                    logFile.flush();
                    logFile.close();
                }
                logFile = fileWriter;
                logFileName = fileName;
            }
    }
    public class ConsoleFocusListener implements java.awt.event.FocusListener {
        public void focusGained(java.awt.event.FocusEvent e) {
            console.setBackground(console.getBackground().brighter());
        }
        public void focusLost(java.awt.event.FocusEvent e) {
            console.setBackground(console.getBackground().darker());
        }
    }
    public static String getHomeDir() {
        return(System.getProperty("user.home"));
    }

    public static String getHomeDir(String defaultPath) {
if(false) {
        String jar = new String("console.jar");
        String path = System.getProperty("java.class.path");
        String dir = System.getProperty("user.dir");
        path = path.replace('\\','/');
        path = path.toLowerCase();
        dir = dir.replace('\\','/');
        defaultPath = defaultPath.replace('\\','/');
        if(path.indexOf(jar)!=-1) {
            /* strip end */
            path = path.substring(0,path.indexOf(jar));
            if(path.length()==0)
                return(dir);
            if((path.charAt(path.length()-1)=='/')||
               (path.charAt(path.length()-1)=='\\')) {
                path = path.substring(0,path.length()-1);
            }
            if(path.length()==0)
                return("");
            /* strip before */
            path = path.substring(
                path.lastIndexOf(System.getProperty("path.separator"))+1,
                path.length());
            /* test for absolute */
            if(path.indexOf(':')!=-1) {
                return(path);
            }
            if(path.charAt(0)=='/') {
                return(path);
            }
            /* get the path before -- if it's not absolute add user.dir */
            /* TODO: if not absolute, is classpath searched? */
            return(dir+"/"+path);
        } else {
            System.out.println("didn't find Terminal in path");
            return(defaultPath);
        }
} else {
            return(defaultPath);
}
    }
    /**
     * Find the path where the Terminal scripts are stored.
     *
     * @@return the path where the scripts are stored
     */
    public static String getScriptDir() {
        return(getHomeDir(System.getProperty("user.dir")+"\\scripts"));
    }
    /**
     * Find the path to the Terminal files
     *
     * @@return the path to the Terminal home directory
     */
    public static String getDir() {
        return(getHomeDir(System.getProperty("user.dir")));
    }
    public static int getByte(byte[] b,int index) {
        return(((int)b[index])&0xff);
    }

}

